// Copyright 2010 Abhishek Kulkarni. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package goprop

import (
	"reflect"
)

func constanter(v reflect.Value) GenericFn {
	return func(reflect.Value) interface{} {
		return constant(v)
	}
}

func squarer(i reflect.Value) interface{} {
	switch i.Kind() {
	case reflect.Float32, reflect.Float64:
		return square
	case reflect.Ptr:
		return squareInterval
	}
	return square
}

func sqrter(i reflect.Value) interface{} {
	switch i.Kind() {
	case reflect.Float32, reflect.Float64:
		return sqrt
	case reflect.Ptr:
		return sqrtInterval
	}
	return sqrt
}

func adder(i reflect.Value) interface{} {
	switch i.Kind() {
	case reflect.Float32, reflect.Float64:
		return add
	case reflect.Ptr:
		return addInterval
	}
	return add
}

func subtractor(i reflect.Value) interface{} {
	switch i.Kind() {
	case reflect.Float32, reflect.Float64:
		return sub
	}
	return sub
}

func multiplier(i reflect.Value) interface{} {
	switch i.Kind() {
	case reflect.Float32, reflect.Float64:
		return mul
	case reflect.Ptr:
		return mulInterval
	}
	return mul
}

func divider(i reflect.Value) interface{} {
	switch i.Kind() {
	case reflect.Float32, reflect.Float64:
		return div
	case reflect.Ptr:
		return divInterval
	}
	return div
}

func constant(a reflect.Value) func() interface{} {
	return func() interface{} {
		return a.Interface()
	}
}

func merger(old, neu interface{}) interface{} {
	switch old.(type) {
	case float64:
		return merge(old.(float64), neu.(float64))
	case *interval:
		return mergeInterval(old.(*interval), neu.(*interval))
	}
	return merge(old.(float64), neu.(float64))
}

// ***

func (pn *PropagatorNetwork) Constant(value interface{}, op *Cell) {
	pn.NewPropagator("constant", constanter(reflect.ValueOf(value)), nil, []*Cell{op})
}

func (pn *PropagatorNetwork) Sum(a, b, c *Cell) {
	pn.NewPropagator("adder", adder, []*Cell{a, b}, []*Cell{c})
	pn.NewPropagator("subtractor", subtractor, []*Cell{c, a}, []*Cell{b})
	pn.NewPropagator("subtractor", subtractor, []*Cell{c, b}, []*Cell{a})
}

func (pn *PropagatorNetwork) Difference(a, b, c *Cell) {
	pn.NewPropagator("subtractor", subtractor, []*Cell{a, b}, []*Cell{c})
	pn.NewPropagator("adder", adder, []*Cell{b, c}, []*Cell{a})
	pn.NewPropagator("subtractor", subtractor, []*Cell{a, c}, []*Cell{b})
}

func (pn *PropagatorNetwork) Product(a, b, c *Cell) {
	pn.NewPropagator("multiplier", multiplier, []*Cell{a, b}, []*Cell{c})
	pn.NewPropagator("divider", divider, []*Cell{c, a}, []*Cell{b})
	pn.NewPropagator("divider", divider, []*Cell{c, b}, []*Cell{a})
}

func (pn *PropagatorNetwork) Division(a, b, c *Cell) {
	pn.NewPropagator("divider", divider, []*Cell{a, b}, []*Cell{c})
	pn.NewPropagator("multiplier", multiplier, []*Cell{b, c}, []*Cell{a})
	pn.NewPropagator("divider", divider, []*Cell{a, c}, []*Cell{b})
}

func (pn *PropagatorNetwork) Quadratic(a, b *Cell) {
	pn.NewPropagator("squarer", squarer, []*Cell{a}, []*Cell{b})
	pn.NewPropagator("sqrter", sqrter, []*Cell{b}, []*Cell{a})
}
